package com.ztom.agent;

import com.ztom.agent.common.PropertyKeys;
import com.ztom.agent.common.PropertyValues;
import com.ztom.agent.config.MyProperties;
import com.ztom.agent.config.TraceConfig;
import com.ztom.agent.config.TraceFilter;
import com.ztom.agent.log.Logger;
import com.ztom.agent.util.ExecutorManager;
import com.ztom.agent.util.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import static com.ztom.agent.common.SystemProperties.LINE_SEPARATOR;

/**
 * @author ZhangTao
 */
public abstract class AbstractBootstrap {

    public final boolean initial() {
        try {
            Logger.info("Thanks sincerely for using MyTraceAgent.");
            if (!doInitial()) {
                Logger.error("AbstractBootstrap doInitial() FAILURE!!!");
                return false;
            }

            printBannerText();
            Logger.info("AbstractBootstrap doInitial() SUCCESS!!!");
            return true;
        } catch (Exception e) {
            Logger.error("AbstractBootstrap.initial()", e);
        }
        return false;
    }

    private boolean doInitial() {
        // 初始化配置文件
        if (!initProperties()) {
            Logger.error("AbstractBootstrap initProperties() FAILURE!!!");
            return false;
        }

        // 初始化系统配置
        if (!initTraceConfig()) {
            Logger.error("AbstractBootstrap initTraceConfig() FAILURE!!!");
            return false;
        }

        // 初始化日志配置
        if (!initLogger()) {
            Logger.error("AbstractBootstrap initLogger() FAILURE!!!");
            return false;
        }

        // 初始化包过滤器配置
        if (!initPackageFilter()) {
            Logger.error("AbstractBootstrap initPackageFilter() FAILURE!!!");
            return false;
        }

        // 初始化方法过滤器配置
        if (!initMethodFilter()) {
            Logger.error("AbstractBootstrap initMethodFilter() FAILURE!!!");
            return false;
        }

        // 初始化系统关闭钩子
        if (!initShutDownHook()) {
            Logger.error("AbstractBootstrap initShutDownHook() FAILURE!!!");
            return false;
        }

        // 初始化ReportScheduler
        if (!initReportScheduler()) {
            Logger.error("AbstractBootstrap initRecorderMaintainer() FAILURE!!!");
            return false;
        }

        // 初始化其他
        if (!initOther()) {
            Logger.error("AbstractBootstrap initOther() FAILURE!!!");
            return false;
        }
        return true;
    }

    private boolean initProperties() {
        InputStream in = null;
        try {
            // 读取配置文件路径, 如果命令行参数未输入, 则使用默认路径下的配置文件
            String configFilePath = System.getProperty(PropertyKeys.CONFIG_FILE_NAME, PropertyValues.DEFAULT_CONFIG_FILE);
            in = new FileInputStream(configFilePath);

            Properties properties = new Properties();
            properties.load(in);
            return MyProperties.initial(properties);
        } catch (IOException e) {
            Logger.error("AbstractBootstrap.initProperties()", e);
        } finally {
            if (Objects.nonNull(in)) {
                IOUtils.closeQuietly(in);
            }
        }
        return false;
    }

    private boolean initTraceConfig() {
        try {
            TraceConfig config = TraceConfig.getInstance();

            initAppName(config);
            initLogConfig(config);
            initFiltersConfig(config);

            config.setPrintDebugLog(MyProperties.getBoolean(PropertyKeys.DEBUG_PRINT_DEBUG_LOG, false));
            config.setDumpClassFile(MyProperties.getBoolean(PropertyKeys.DUMP_UPDATED_CLASS_FILE, false));
            config.setDumpClassFilePath(MyProperties.getStr(PropertyKeys.DUMP_UPDATED_CLASS_FILE_PATH, PropertyValues.DEFAULT_DUMP_UPDATED_CLASS_FILE_PATH));
            return true;
        } catch (Exception e) {
            Logger.error("AbstractBootstrap.initTraceConfig()", e);
        }
        return false;
    }

    private void initAppName(TraceConfig config) {
        String appName = MyProperties.getStr(PropertyKeys.APP_NAME);
        if (StringUtils.isBlank(appName)) {
            throw new IllegalArgumentException("AppName is required!!!");
        }
        config.setAppName(appName);
    }

    private void initLogConfig(TraceConfig config) {
        config.setLogRollingTimeUnit(MyProperties.getStr(PropertyKeys.LOG_ROLLING_TIME_TIME_UNIT, PropertyValues.LOG_ROLLING_TIME_DAILY));
        config.setLogReserveCount(MyProperties.getInt(PropertyKeys.LOG_RESERVE_COUNT, PropertyValues.DEFAULT_LOG_RESERVE_COUNT));
        config.setLogFilePath(MyProperties.getStr(PropertyKeys.LOG_FILE_PATH, PropertyValues.DEFAULT_LOG_FILE_PATH));
    }

    private void initFiltersConfig(TraceConfig config) {
        String includePackages = MyProperties.getStr(PropertyKeys.FILTER_INCLUDE_PACKAGES, "");
        if (StringUtils.isBlank(includePackages)) {
            throw new IllegalArgumentException("IncludePackages is required!!!");
        }

        config.setIncludePackages(includePackages);
        config.setPlugins(MyProperties.getStr(PropertyKeys.PLUGINS, ""));
        config.setExcludePackages(MyProperties.getStr(PropertyKeys.FILTER_EXCLUDE_PACKAGES, ""));
        config.setExcludeMethods(MyProperties.getStr(PropertyKeys.FILTER_EXCLUDE_METHODS, ""));
        config.setExcludePrivateMethod(MyProperties.getBoolean(PropertyKeys.EXCLUDE_PRIVATE_METHODS, true));
    }

    private boolean initLogger() {
        try {
            Logger.setDebugEnable(TraceConfig.getInstance().isPrintDebugLog());
            return true;
        } catch (Exception e) {
            Logger.error("AbstractBootstrap.initLogger()", e);
        }
        return false;
    }

    private boolean initPackageFilter() {
        try {
            // 添加配置的要扫描的包
            String includePackages = TraceConfig.getInstance().getIncludePackages();
            String[] includeArr = includePackages.split(PropertyValues.FILTER_SEPARATOR);
            for (String pkg : includeArr) {
                TraceFilter.addIncludePackage(pkg);
            }

            // 将插件添加到includePackages中
            String plugins = TraceConfig.getInstance().getPlugins();
            TraceFilter.addPlugins(plugins);

            // 添加配置的不扫描的包
            String excludePackages = TraceConfig.getInstance().getExcludePackages();
            String[] excludeArr = excludePackages.split(PropertyValues.FILTER_SEPARATOR);
            for (String pkg : excludeArr) {
                TraceFilter.addExcludePackage(pkg);
            }
            return true;
        } catch (Exception e) {
            Logger.error("AbstractBootstrap.initPackageFilter()", e);
        }
        return false;
    }

    private boolean initMethodFilter() {
        try {
            String excludeMethods = TraceConfig.getInstance().getExcludeMethods();
            String[] excludeArr = excludeMethods.split(PropertyValues.FILTER_SEPARATOR);
            for (String method : excludeArr) {
                TraceFilter.addExcludeMethods(method);
            }
            return true;
        } catch (Exception e) {
            Logger.error("AbstractBootstrap.initMethodFilter()", e);
        }
        return false;
    }

    private boolean initShutDownHook() {
        try {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                Logger.info("ENTER ShutdownHook...");
                try {
                    ExecutorManager.stopAll(6, TimeUnit.SECONDS);
                } finally {
                    Logger.info("EXIT ShutdownHook...");
                }
            }));
            return true;
        } catch (Exception e) {
            Logger.error("AbstractBootstrap.initShutDownHook()", e);
        }
        return false;
    }

    private boolean initReportScheduler() {
        try {
//            ReportScheduler.initScheduleTask();
            return true;
        } catch (Exception e) {
            Logger.error("AbstractBootstrap.initReportScheduler()", e);
        }
        return false;
    }

    /**
     * 初始化其他配置
     *
     * @return
     */
    public abstract boolean initOther();

    private void printBannerText() {
        Logger.info(LINE_SEPARATOR +
                " /__  ___/                                " + LINE_SEPARATOR +
                "   / /   __      ___      ___      ___    " + LINE_SEPARATOR +
                "  / /  //  ) ) //   ) ) //   ) ) //___) ) " + LINE_SEPARATOR +
                " / /  //      //   / / //       //        " + LINE_SEPARATOR +
                "/ /  //      ((___( ( ((____   ((____     " + LINE_SEPARATOR);
    }
}
